<template>
  <canvas
      id="canvas"
      @mousedown="mouseDown"
      @mousemove="mouseMove"
      @mouseup="mouseUp"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
  ></canvas>
</template>

<script lang="ts" setup>
const props = defineProps({
  width: {
    type: Number,
    default: 800,
  },
  height: {
    type: Number,
    default: 300,
  },
  lineWidth: {
    type: Number,
    default: 4,
  },
  lineColor: {
    type: String,
    default: '#000000',
  },
  bgColor: {
    type: String,
    default: '',
  },
  isCrop: {
    type: Boolean,
    default: false,
  },
  isClearBgColor: {
    type: Boolean,
    default: true,
  },
});
let canvas: any = null; //document.getElementById('canvas')
let hasDrew = false;
let resultImg = '';
let points: any = [];
let canvasTxt: any = null;
let startX = 0;
let startY = 0;
let isDrawing = false;
let sratio: any = 1;

onBeforeMount(() => {
  window.addEventListener('resize', resizeHandler);
});
onUnmounted(() => {
  window.removeEventListener('resize', resizeHandler);
});

onMounted(() => {
  //   const canvas = this.$refs.canvas
  canvas = document.getElementById('canvas');
  canvas.height = props.height;
  canvas.width = props.width;
  canvas.style.background = myBg.value;
  resizeHandler();
  // 在画板以外松开鼠标后冻结画笔
  document.onmouseup = () => {
    isDrawing = false;
  };
});

const ratio: any = computed(() => {
  return props.height / props.width;
});

const stageInfo: any = computed(() => {
  return canvas.getBoundingClientRect();
});
const myBg = computed(() => {
  return props.bgColor ? props.bgColor : 'rgba(255, 255, 255, 0)';
});

watch(myBg, (newVal) => {
  console.log('bg change', newVal);
  canvas.style.background = newVal;
});

const resizeHandler = () => {
  canvas.style.width = props.width + 'px';
  const realw = parseFloat(window.getComputedStyle(canvas).width);
  canvas.style.height = ratio.value * realw + 'px';
  canvasTxt = canvas.getContext('2d');
  canvasTxt.scale(1 * sratio, 1 * sratio);
  sratio = realw / props.width;
  canvasTxt.scale(1 / sratio, 1 / sratio);
};
// pc
const mouseDown = (e) => {
  e = e || event;
  e.preventDefault();
  isDrawing = true;
  hasDrew = true;
  let obj = {
    x: e.offsetX,
    y: e.offsetY,
  };
  drawStart(obj);
};
const mouseMove = (e) => {
  e = e || event;
  e.preventDefault();
  if (isDrawing) {
    let obj = {
      x: e.offsetX,
      y: e.offsetY,
    };
    drawMove(obj);
  }
};
const mouseUp = (e) => {
  e = e || event;
  e.preventDefault();
  let obj = {
    x: e.offsetX,
    y: e.offsetY,
  };
  drawEnd(obj);
  isDrawing = false;
};
// mobile
const touchStart = (e) => {
  e = e || event;
  e.preventDefault();
  hasDrew = true;
  if (e.touches.length === 1) {
    let obj = {
      x: e.targetTouches[0].clientX - canvas.getBoundingClientRect().left,
      y: e.targetTouches[0].clientY - canvas.getBoundingClientRect().top,
    };
    drawStart(obj);
  }
};
const touchMove = (e) => {
  e = e || event;
  e.preventDefault();
  if (e.touches.length === 1) {
    let obj = {
      x: e.targetTouches[0].clientX - canvas.getBoundingClientRect().left,
      y: e.targetTouches[0].clientY - canvas.getBoundingClientRect().top,
    };
    drawMove(obj);
  }
};
const touchEnd = (e) => {
  e = e || event;
  e.preventDefault();
  if (e.touches.length === 1) {
    let obj = {
      x: e.targetTouches[0].clientX - canvas.getBoundingClientRect().left,
      y: e.targetTouches[0].clientY - canvas.getBoundingClientRect().top,
    };
    drawEnd(obj);
  }
};
// 绘制
const drawStart = (obj) => {
  startX = obj.x;
  startY = obj.y;
  canvasTxt.beginPath();
  canvasTxt.moveTo(startX, startY);
  canvasTxt.lineTo(obj.x, obj.y);
  canvasTxt.lineCap = 'round';
  canvasTxt.lineJoin = 'round';
  canvasTxt.lineWidth = props.lineWidth * sratio;
  canvasTxt.stroke();
  canvasTxt.closePath();
  points.push(obj);
};
const drawMove = (obj) => {
  canvasTxt.beginPath();
  canvasTxt.moveTo(startX, startY);
  canvasTxt.lineTo(obj.x, obj.y);
  canvasTxt.strokeStyle = props.lineColor;
  canvasTxt.lineWidth = props.lineWidth * sratio;
  canvasTxt.lineCap = 'round';
  canvasTxt.lineJoin = 'round';
  canvasTxt.stroke();
  canvasTxt.closePath();
  startY = obj.y;
  startX = obj.x;
  points.push(obj);
};
const drawEnd = (obj) => {
  canvasTxt.beginPath();
  canvasTxt.moveTo(startX, startY);
  canvasTxt.lineCap = 'round';
  canvasTxt.lineJoin = 'round';
  canvasTxt.stroke();
  canvasTxt.closePath();
  points.push(obj);
  points.push({x: -1, y: -1});
};
// 操作
const generate = () => {
  const pm = new Promise((resolve, reject) => {
    if (!hasDrew) {
      reject(`Warning: Not Signned!`);
      return;
    }
    var resImgData = canvasTxt.getImageData(0, 0, canvas.width, canvas.height);
    canvasTxt.globalCompositeOperation = 'destination-over';
    canvasTxt.fillStyle = myBg.value;
    canvasTxt.fillRect(0, 0, canvas.width, canvas.height);
    resultImg = canvas.toDataURL();
    var resultImg = resultImg;
    canvasTxt.clearRect(0, 0, canvas.width, canvas.height);
    canvasTxt.putImageData(resImgData, 0, 0);
    canvasTxt.globalCompositeOperation = 'source-over';
    if (props.isCrop) {
      const crop_area = getCropArea(resImgData.data);
      var crop_canvas: any = document.createElement('canvas');
      const crop_ctx: any = crop_canvas.getContext('2d');
      crop_canvas.width = crop_area[2] - crop_area[0];
      crop_canvas.height = crop_area[3] - crop_area[1];
      const crop_imgData = canvasTxt.getImageData(...crop_area);
      crop_ctx.globalCompositeOperation = 'destination-over';
      crop_ctx.putImageData(crop_imgData, 0, 0);
      crop_ctx.fillStyle = myBg.value;
      crop_ctx.fillRect(0, 0, crop_canvas.width, crop_canvas.height);
      resultImg = crop_canvas.toDataURL();
      crop_canvas = null;
    }
    resolve(resultImg);
  });
  return pm;
};

const emits = defineEmits(['update:bgColor']);
const reset = () => {
  canvasTxt.clearRect(0, 0, canvas.width, canvas.height);
  if (props.isClearBgColor) {
    // this.$emit('update:bgColor', '')
    emits('update:bgColor', '');
    canvas.style.background = 'rgba(255, 255, 255, 0)';
  }
  points = [];
  hasDrew = false;
  resultImg = '';
};
const getCropArea = (imgData) => {
  var topX = canvas.width;
  var btmX = 0;
  var topY = canvas.height;
  var btnY = 0;
  for (var i = 0; i < canvas.width; i++) {
    for (var j = 0; j < canvas.height; j++) {
      var pos = (i + canvas.width * j) * 4;
      if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
        btnY = Math.max(j, btnY);
        btmX = Math.max(i, btmX);
        topY = Math.min(j, topY);
        topX = Math.min(i, topX);
      }
    }
  }
  topX++;
  btmX++;
  topY++;
  btnY++;
  const data = [topX, topY, btmX, btnY];
  return data;
};

defineExpose({reset, generate});
</script>

<style scoped>
canvas {
  max-width: 100%;
  display: block;
}
</style>
